//-------------------------------------------------------------------
//	Copyright (c) 2012-2014 Zhirnov Andrey
//	This file is part of the "UXGEN-ES" project.
//	See copyright notice in "Engine.h".
//-------------------------------------------------------------------

#include "Graphics/Loaders/ProgramLoader.h"
#include "Graphics/MetaData/Meta.Program.h"
#include "Graphics/Systems/GraphicsEngine.h"

namespace Engine
{
namespace Graphics
{
	
/*
=================================================
	constructor
=================================================
*/
	ProgramLoader::ProgramLoader ()
	{
	}
	
/*
=================================================
	Register
=================================================
*/
	void ProgramLoader::Register (ResourceManager *rm)
	{
		rm->RegisterLoader( EResource::PROGRAM, Load, null, this );
	}
	
/*
=================================================
	Unregister
=================================================
*/
	void ProgramLoader::Unregister (ResourceManager *rm)
	{
		rm->UnregisterLoader( EResource::PROGRAM, Load, null, this );
	}

/*
=================================================
	_Load
=================================================
*/
	bool ProgramLoader::_Load (ResourcePtr &resource, const RFilePtr &rfile, void *parameter,
								const Program::ProgramLoadParams &loadParams, const EngineSubSystemsRef ess)
	{
		RESFILE_CHECK_HEADER( rfile, ProgramFileInfo );

		uint	unique_key = 0;
		rfile->Read( unique_key );

		RFilePtr	file;

		if ( unique_key != 0 )
		{
			CHECK_ERR( loadParams.hasPassword );

			Program::ProgramLoadParams::password_t	pw = loadParams.password;
			_shared_resources_::CryptKey::BuildKey( pw, unique_key );

			File::SimpleWCryptFile::CryptAlgorithm	alg( pw );

			CHECK_ERR( (file = File::SimpleRCryptFile::New( rfile, alg )).IsNotNull() );
		}
		else
		{
			file = rfile;
		}

		MetaData::ProgramCfg	prog;
		CHECK_ERR( prog.Deserialize( file ) );

		ProgramPtr		program = Program::New( ess );
		ShaderProgram &	sp		= program->GetShaderProgram();

		Shader		fs( gl_shader::FRAGMENT );
		Shader		vs( gl_shader::VERTEX );

		CHECK_ERR( ess->GetGraphicsEngine()->GetShaderManager()->CreateShader( fs, prog.fragmentShader ) );
		CHECK_ERR( ess->GetGraphicsEngine()->GetShaderManager()->CreateShader( vs, prog.vertexShader ) );

		sp.Attach( fs );
		sp.Attach( vs );

		FOR( i, prog.attribs )
		{
			sp.BindAttrib( prog.attribs[i].name.cstr(), prog.attribs[i].index );
			program->_AddAttrib( i, prog.attribs[i].index, (gl_vertex_attrib::type) prog.attribs[i].type );
		}

		if ( not sp.Link() )
		{
			sp.ShowLog();
			RETURN_ERR( "shader program linking failed!" );
		}
		
		if ( not prog.frag_ub_name.Empty() )
		{
			CHECK_ERR( program->_fragUB.GetLocation( sp, prog.frag_ub_name.cstr() ) );
		}

		FOR( i, prog.textures )
		{
			Sampler	samp;
			prog.textures[i].sampler.Init( &samp, ess );

			program->AddTexture( prog.textures[i].uniform.cstr(), samp );
		}

		RenderState	rs;

		prog.renderState.Init( &rs, ess );
		program->SetRenderState( rs );

		program->_memSize = prog.fragmentShader.Length() + prog.vertexShader.Length();

		resource = program;
		return true;
	}
	
/*
=================================================
	Load
=================================================
*/
	bool ProgramLoader::Load (ResourcePtr &resource, const RFilePtr &file, void *parameter,
							  const Resource::ResLoadParams &loadParams, const EngineSubSystemsRef ess)
	{
		CHECK_ERR( loadParams.Is< Program::ProgramLoadParams >() );

		return _Load( resource, file, parameter, loadParams.To< Program::ProgramLoadParams >(), ess );
	}
	
}	// Graphics
}	// Engine